#include <fstream>
#include <stdio.h>
#include <string>
#include <map>

#include "rectype.h"
#include "recbase.h"
#include "stdffield.h"
#include "stdffield.cpp"

#include "util.h"
#include "stdfrecscont.h"

#include "cnfield.cpp"
#include "nmfield.cpp"
#include "nmarrfield.cpp"
#include "cnarrfield.cpp"
#include "gdrstdffield.h"
#include "fileprintdetail.h"

#include "far.h"
#include "atr.h"
#include "mir.h"
#include "mrr.h"
#include "pcr.h"
#include "hbr.h"
#include "sdr.h"
#include "sbr.h"
#include "pmr.h"
#include "pgr.h"
#include "plr.h"
#include "rdr.h"
#include "wir.h"
#include "wrr.h"
#include "wcr.h"
#include "pir.h"
#include "prr.h"
#include "tsr.h"
#include "ptr_xy.h"
#include "bps.h"
#include "eps.h"
#include "gdr.h"
#include "dtr.h"
#include "ftr.h"
#include "mpr.h"
#include "stdfconfig.h"

#include "outformat.h"
#include <algorithm>
#include <vector>


using namespace std;
using namespace opsrc;
using namespace opsrc::stdf;

typedef struct XYPoint_TYP{
    int x;
    int y;
}PosTyp;


inline bool lesser_than_int(int val1, int val2) { return val1 < val2; }

std::string CreateKey(int val1, int val2){
    stringstream ss;
    ss << "[" << val1 << "," << val2 << "]";
    string key = ss.str();
    return key;
}


int StdfRecsCont::orderindex = 0;

StdfRecsCont::StdfRecsCont() {
}

StdfRecsCont::~StdfRecsCont() {
}

endian StdfRecsCont::getEndian() const {
    return this->edi;
}

void StdfRecsCont::setEndian(endian edi) {
    this->edi = edi;
}


bool StdfRecsCont::createRec(ifstream* file, bool decodeFields) {

    // Read stdf record byte stream
    unsigned char ubuffer [4];
    std::ios::pos_type recfirstbyteposition = file->tellg();
    file->read((char*)ubuffer, 4);

    stdf_rec r = StdfHelp::checkStdfRecHeader((char*)&ubuffer[2]);
    if ( r == UNK )
    {
#if _DEBUG
        cout << "Invalid header detected" << endl;
#endif
        return NULL;
    }

    unsigned short int reclen = 0;
    // little endian processing, swap byte order
    // rec length is only 2 byte long
    if ( getEndian() == little_endian )
        Util::swapByteChar(&ubuffer[0], 2);

    bool ok;
    reclen = Util::convertCharToShortInt(&ubuffer[0]);
    RecBase* rp =  this->implcreateRec(file, r, reclen, recfirstbyteposition, ok);
    if(rp){
        of->out(rp);
        cont[r].push_back(rp);
    }
    return ok;
}

void StdfRecsCont::saveRecords()
{
    if( StdfConfig::instance()->getOutputMode() == "D_FILE" ){
        cout << "saveRecords: init" << endl;

        FilePrintDetail* fpd;
        if (of) {
            if( of->outType() != 1 )    return;
            fpd =  dynamic_cast< FilePrintDetail* >(of);
        }
        cout << "saveRecords: init-100" << endl;

        Mir* pMir = dynamic_cast<Mir*>(*cont[MIR].begin());
        cout << "saveRecords: init-200" << endl;
        if(pMir){
            fpd->setLotID( pMir->getLotId().getData() );
        }
        cout << "saveRecords: init-300" << endl;

        Wir* pWir = dynamic_cast<Wir*>(*cont[WIR].begin());
        if(pWir){
            fpd->setWaferID( pWir->getWaferId().getData() );
        }
        cout << "saveRecords: init-500" << endl;

        std::vector< int >testNumVec;
        std::map<string, PosTyp> prrXYMap;
        std::vector<RecBase*>::iterator it_rec;
        std::vector<RecBase*> ptrVec = cont[PTR];
        std::vector<RecBase*> bpsVec = cont[BPS];
        std::vector<RecBase*> epsVec = cont[EPS];
        std::vector<RecBase*> prrVec = cont[PRR];

        int prrPos = 0;
        int ptrPos = 0;

        bool bAddTestNum = (StdfConfig::instance()->getTestNumbersSetting().size()==0);
        testNumVec.clear();

        cout << "saveRecords: initialized" << endl;

        if( epsVec.size() == bpsVec.size() ){
            for(unsigned int i=0; i<epsVec.size(); ++i){
                cout << "saveRecords: inside a for " << i << endl;
                int upperVal = 0;
                int lowerVal = 0;

                if(i<bpsVec.size()-1){
                    Bps* pBps = dynamic_cast<Bps*>(bpsVec[i+1]);
                    if(pBps){
                        upperVal = pBps->getRecOrder();
                    }
                }
                else{
                    upperVal = prrVec.back()->getRecOrder();
                }

                Eps* pEps = dynamic_cast<Eps*>(epsVec[i]);
                if(pEps){
                    lowerVal = pEps->getRecOrder();
                }

                //Get the num of between the upperVal and lowerval
                unsigned int prrNum = 0;
                prrXYMap.clear();
                for(it_rec=prrVec.begin()+prrPos; it_rec!=prrVec.end(); ++it_rec){
                    Prr* pPrr = dynamic_cast<Prr*>(*it_rec);
                    const int orderIdx = pPrr->getRecOrder();
                    if( (orderIdx > lowerVal) && (orderIdx<=upperVal) ){
                        int head_num =  pPrr->getHeadNumber().getData();
                        int site_num = pPrr->getSiteNumber().getData();
                        PosTyp xypos;
                        xypos.x = pPrr->getXCoordinate().getData();
                        xypos.y = pPrr->getYCoordinate().getData();
                        std::string key = ::CreateKey(head_num, site_num);
                        prrXYMap[key] = xypos;
                        prrNum++;
                    }else {
                        if(orderIdx>upperVal){
                            break;
                        }
                    }
                }

                if( prrNum != prrXYMap.size()){
                    printf("Gotchas size err");
                }
                int cnt = 0;
                //reassign the upper and lower range
                upperVal = lowerVal;
                lowerVal = (bpsVec[i])->getRecOrder();

                for(it_rec=ptrVec.begin()+ptrPos; it_rec!=ptrVec.end(); ++it_rec, ++ptrPos, ++cnt){
                    PtrXY* pPtr = dynamic_cast<PtrXY*>(*it_rec);
                    const int orderIdx = pPtr->getRecOrder();
                    if( (orderIdx > lowerVal) && (orderIdx<upperVal) ){
                        const int mod = cnt % prrNum;
                        Prr* pPrr = dynamic_cast<Prr*>(prrVec[prrPos+mod]);

                        int head_num =  pPtr->getHeadNumber().getData();
                        int site_num = pPtr->getSiteNumber().getData();
                        std::string key = ::CreateKey(head_num, site_num);

                        if(prrXYMap.find(key) != prrXYMap.end() ){
                            PosTyp xypos = prrXYMap[key];
                            StdfField<int> xval, yval;
                            xval.setData(xypos.x);
                            yval.setData(xypos.y);
                            pPtr->setXCoordinate(xval);
                            pPtr->setYCoordinate(yval);
                            //pPtr->setXYCoordinate( xval, yval);
                        }
                        else{
                            pPtr->setXCoordinate( pPrr->getXCoordinate());
                            pPtr->setYCoordinate(pPrr->getYCoordinate());
                            //pPtr->setXYCoordinate(pPrr->getXCoordinate(), pPrr->getYCoordinate());
                        }

                        if(bAddTestNum){
                            long tnum = pPtr->getTestNumber().getData();
                            testNumVec.push_back(tnum);
                        }
                    }else {
                        if(orderIdx>upperVal){
                            int numofset = cnt / prrNum;
                            int remainder = cnt % prrNum;
                            if(remainder != 0){
                                printf("\nNum of set = %d, remainder = %d", numofset, remainder);
                            }
                            break;
                        }
                    }
                }
                prrPos += prrNum;
            }
        } else {
            cout << "BPS and EPS size mismatched!" << endl;
        }

        cout << "saveRecords: processed" << endl;

        std::sort(testNumVec.begin(), testNumVec.end());
        testNumVec.erase(std::unique(testNumVec.begin(), testNumVec.end()), testNumVec.end());
        StdfConfig::instance()->setTestNumbers(testNumVec);
        fpd->saveToFile( cont[FAR], "far.txt");
        fpd->saveToFile( cont[ATR], "atr.txt");
        fpd->saveToFile( cont[MIR], "mir.txt");
        fpd->saveToFile( cont[MRR], "mrr.txt");
        fpd->saveToFile( cont[PCR], "pcr.txt");
        fpd->saveToFile( cont[HBR], "hbr.txt");
        fpd->saveToFile( cont[SBR], "sbr.txt");
        fpd->saveToFile( cont[PMR], "pmr.txt");
        fpd->saveToFile( cont[PGR], "pgr.txt");
        fpd->saveToFile( cont[PLR], "plr.txt");
        fpd->saveToFile( cont[RDR], "rdr.txt");
        fpd->saveToFile( cont[SDR], "sdr.txt");
        fpd->saveToFile( cont[WIR], "wir.txt");
        fpd->saveToFile( cont[WRR], "wrr.txt");
        fpd->saveToFile( cont[WCR], "wcr.txt");
        fpd->saveToFile( cont[PIR], "pir.txt");
        fpd->saveToFile( cont[PRR], "prr.txt");
        fpd->saveToFile( cont[TSR], "tsr.txt");
        fpd->saveToFile( cont[PTR], "ptr.txt");
        fpd->saveToFile( cont[MPR], "mpr.txt");
        fpd->saveToFile( cont[FTR], "ftr.txt");
        fpd->saveToFile( cont[BPS], "bps.txt");
        fpd->saveToFile( cont[EPS], "eps.txt");
        fpd->saveToFile( cont[GDR], "gdr.txt");
        fpd->saveToFile( cont[DTR], "dtr.txt");
        fpd->savePtrRecord(cont[PTR]);

        cout << "saveRecords: wrote to files" << endl;
    }
}

RecBase* StdfRecsCont::implcreateRec(ifstream* file, stdf_rec stdfrec, int dataByteLength, std::ios::pos_type stdfFileRcFirstBytePosition, bool& ok)
{
    RecBase* prec = NULL;
    ok = true;
    if( stdfrec == UNK)
    {
        ok = false;
        return NULL;
    }
    char buffer [dataByteLength];
    file->read(buffer, dataByteLength);

    if( StdfConfig::instance()->getRecSetting(stdfrec) )
    {
        switch (stdfrec)
        {
        case FAR:
            prec = new Far(FAR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++, edi, &buffer[0]);
            break;

        case ATR:
            prec = new Atr(ATR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;

        case MIR:
            prec = new Mir(MIR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;

        case MRR:
            prec = new Mrr(MRR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PCR:
            prec = new Pcr(PCR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case HBR:
            prec = new Hbr(HBR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case SBR:
            prec = new Sbr(SBR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PMR:
            prec = new Pmr(PMR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PGR:
            prec = new Pgr(PGR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PLR:
            prec = new Plr(PLR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case RDR:
            prec = new Rdr(RDR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case SDR:
            prec = new Sdr(SDR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case WIR:
            prec = new Wir(WIR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case WRR:
            prec = new Wrr(WRR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case WCR:
            prec = new Wcr(WCR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PIR:
            prec = new Pir(PIR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PRR:
            prec = new Prr(PRR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case TSR:
            prec = new Tsr(TSR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case PTR:
            prec = new PtrXY(PTR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case MPR:
            prec = new Mpr(MPR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case FTR:
            prec = new Ftr(FTR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case BPS:
            prec = new Bps(BPS, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case EPS:
            prec = new Eps(EPS, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case GDR:
            prec = new Gdr(GDR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
        case DTR:
            prec = new Dtr(DTR, dataByteLength, stdfFileRcFirstBytePosition, orderindex++,  edi, &buffer[0]);
            break;
            //case UNK:
        default:
            ok = false;
            // do nothing, shut compiler warning
            break;       
        };
    }
    return prec;
}


void StdfRecsCont::setOutFormat(OutFormat*& out){
    of = out;
}
